package abstractGA.popu;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import abstractGA.GAParameter;
import abstractGA.chrom.Chrom;
import foundation.fileUtil.DataCell;

/**
 * 种群类
 */
public abstract class Population {

	// 染色体的数组列表
	protected List<Chrom> chroms = new ArrayList<Chrom>();
	// pareTo最优染色体集
	protected List<Chrom> bestChroms = new ArrayList<Chrom>();
	// 算法参数
	protected GAParameter parameter = null;
	// 随机数类
	protected Random random = new Random();

	// 产生空种群
	protected Population() {

	}

	// 获取最优染色体集
	public abstract List<Chrom> getBestChroms();

	/**
	 * 轮盘赌选择
	 * 
	 * @param weights
	 *            :权重数组，所有列表元素之各应为1
	 * @return：被选中下标的数组
	 */
	public int[] rouletteWheelChoose(float[] weights, int size) {
		int len = weights.length;
		if (size > len)
			size = len;
		int[] chooseResults = new int[size];

		// 按累积概率值进行轮盘赌选择
		int cur = 0;
		while (cur < size) {
			float rnum = random.nextFloat();
			float accum = 0.0f;
			for (int i = 0; i < len; i++) {
				if (Float.compare(rnum, accum) == 0
						|| rnum < accum + weights[i]) {
					chooseResults[cur] = i;
					break;
				}
				accum += weights[i];
			}
			cur++;
		}
		return chooseResults;
	}

	/**
	 * 种群的变异操作
	 * 
	 * @param mutateNum
	 *            ：变异次数，目前仅实现单点均匀变异
	 */
	public void mutate(int mutateNum) {
		for (Chrom chrom : chroms) {
			chrom.mutate(mutateNum);
		}	
		bestChroms.clear();
	}
	/**
	 * 进行交叉操作
	 * 
	 * @param srcChrom
	 * @param desChrom
	 * @param crossPos
	 * @return 交叉过后的染色体编码codes集
	 */
	public List<List<StringBuffer>> doCrossover(Chrom srcChrom, Chrom desChrom,
			int crossPos) {

		int chromLen = parameter.getChromLen();
		// 源染色体的编码
		List<StringBuffer> srcEncodes = srcChrom.getCodes();
		List<StringBuffer> newSrcCodes=new ArrayList<StringBuffer>();
		for(StringBuffer srcEncode: srcEncodes){
			newSrcCodes.add(srcEncode);
		}
		// 目标染色体的编码
		List<StringBuffer> desEncodes = desChrom.getCodes();
		List<StringBuffer> newDestCodes=new ArrayList<StringBuffer>();
		for(StringBuffer desEncode: desEncodes){
			newDestCodes.add(desEncode);
		}
		// 临时编码
		List<StringBuffer> tempEncodes = new ArrayList<StringBuffer>();
		// 需要返回的交叉过后的染色体编码集合
		List<List<StringBuffer>> crossOveredChroms = new ArrayList<List<StringBuffer>>();

		// 复制源染色体的后半部分编码到临时编码
		for (int j = crossPos; j < chromLen; j++) {
			String tempStr = new String(newSrcCodes.get(j).toString());
			StringBuffer tempSB = new StringBuffer(tempStr);
			tempEncodes.add(tempSB);
		}
		// 修改源染色体编码的后半部分
		for (int m = crossPos; m < chromLen; m++) {
			String tempStr = new String(newDestCodes.get(m).toString());
			StringBuffer tempSB = new StringBuffer(tempStr);
			newSrcCodes.set(m, tempSB);
		}

		// 修改目标染色体编码的后半部分
		for (int k = crossPos; k < chromLen; k++) {
			String tempStr = new String(tempEncodes.get(k - crossPos)
					.toString());
			StringBuffer tempSB = new StringBuffer(tempStr);
			newDestCodes.set(k, tempSB);
		}

		crossOveredChroms.add(newSrcCodes);
		crossOveredChroms.add(newDestCodes);
		return crossOveredChroms;

	}

	/**
	 * 
	 * @param crossoverNum
	 *            ：交叉的数目
	 */
	public void crossover(int crossoverNum) {
		// 种群大小
		int popSize = chroms.size();
		// 产生交叉的位置
		int chromLen = parameter.getChromLen();
		// 获取交叉概率
		float crossProb = parameter.getCrossProb();
		boolean isCrossed = false;
		// 随机对种群的两个个体进交叉操作
		for (int i = 0; i < popSize && popSize >= 2; i++) {
			// 随机判断第i个体是否参与交叉
			float draw = random.nextFloat();
			// 第i个体参与交叉
			if (Float.compare(draw,crossProb)<0) {
				Chrom srcChrom = chroms.get(i).copy();
				// 随机产生另一个交叉个体
				int otherIdx = random.nextInt(popSize);
				// 避免自身交叉
				while (otherIdx == i) {
					otherIdx = random.nextInt(popSize);
				}
				Chrom desChrom = chroms.get(otherIdx).copy();
				int crossPos = random.nextInt(chromLen);
				while (crossPos == 0) {// 防止第0位交叉，种群不变化
					crossPos = random.nextInt(chromLen);
				}

				List<List<StringBuffer>> tempChroms = doCrossover(srcChrom,
						desChrom, crossPos);
				srcChrom.setCodes(tempChroms.get(0));
				desChrom.setCodes(tempChroms.get(1));

				chroms.set(i, srcChrom);
				chroms.set(otherIdx, desChrom);
				isCrossed = true;

			}
		}
		if (isCrossed) {
			bestChroms.clear();
		}
	}

	/**
	 * 向种群中增加染色体集
	 * 
	 * @param chroms1
	 *            ：染色体集
	 */
	public void add(List<Chrom> chroms1) {
		int size = chroms1.size();
		if (size > 0) {
			bestChroms.clear();
			for (Chrom newChrom : chroms1) {
				getChroms().add(newChrom.copy());
			}
		}
	}

	/**
	 * 交配选择
	 * 
	 * @param chroms
	 *            :染色体集合
	 * @param 返回
	 *            ：选中染色体下标的数组
	 */
	public abstract int[] mutingChoose(List<Chrom> chroms);

	/**
	 * @return:返回交配选择后的种群
	 */
	public Population mutingChoose() {
		Population chosenPopu = copy();
		// 获取被选择染色体的下标
		int idxs[] = mutingChoose(chroms);
		// 产生选择后种群的染色体
		List<Chrom> chosenChroms = chosenPopu.getChroms();
		int len = chroms.size();
		for (int i = 0; i < len; i++) {
			chosenChroms.set(i, chroms.get(idxs[i]).copy());
		}
		chosenPopu.getBestChroms().clear();
		return chosenPopu;
	}

	/**
	 * 环境选择
	 * 
	 * @param tempPopu
	 *            ：临时种群
	 * @return：环境选择后的种群
	 */
	public abstract Population envSelect(Population tempPopu);

	// 种群复制
	public abstract Population copy();

	// 种群中各染色体对应的字符串
	public List<String> getChromsEncodeStrs() {
		List<String> chromsEnncodeStrs = new ArrayList<String>();
		int len = chroms.size();
		for (int i = 0; i < len; i++) {
			List<StringBuffer> encodes = chroms.get(i).getCodes();
			String codeStr = encodes.toString();
			chromsEnncodeStrs.add(codeStr);
		}

		return chromsEnncodeStrs;
	}

	/**
	 * 按 population1 chrom1-[1,2,0,3,...]:fitness 格式输出
	 * 
	 * @param genNum
	 *            ：第几代种群
	 * @return
	 */
	public List<List<DataCell>> outPopulation(int genNum) {
		int size = chroms.size();
		List<List<DataCell>> popuRows = new ArrayList<List<DataCell>>();

		List<DataCell> titleRow = new ArrayList<DataCell>();
		DataCell titleCell = new DataCell();
		titleCell.setName("Popution" + String.valueOf(genNum));
		titleCell.setValue("Popution" + String.valueOf(genNum) + "：");
		titleRow.add(titleCell);
		popuRows.add(titleRow);

		for (int i = 0; i < size; i++) {
			List<DataCell> chromRow = new ArrayList<DataCell>();
			DataCell chromCell = new DataCell();
			Chrom curChrom = chroms.get(i);
			List<StringBuffer> curCodes = curChrom.getCodes();
			boolean isBestChrom = false;
			for (Chrom bestChrom : bestChroms) {
				List<StringBuffer> bestCodes = bestChrom.getCodes();
				if (isEqual(curCodes, bestCodes)) {
					isBestChrom = true;
				}
			}
			if (isBestChrom) {
				chromCell.setName("bestChrom" + i + "*");
			} else {
				chromCell.setName("chrom" + i);
			}

			chromCell.setValue(curChrom.outChrom());
			chromRow.add(chromCell);
			popuRows.add(chromRow);
		}
		return popuRows;
	}

	/**
	 * 判定两个染色体的编码是否相同
	 * 
	 * @param srcCode
	 *            :源编码
	 * @param tgtCode
	 *            :目标编码
	 * @return
	 */
	public boolean isEqual(List<StringBuffer> srcCode,
			List<StringBuffer> tgtCode) {
		boolean retVal = false;
		int size1 = srcCode.size();
		int size2 = tgtCode.size();
		if (size1 == size2) {
			retVal = true;
			for (int i = 0; i < size1; i++) {
				String str1 = srcCode.get(i).toString();
				String str2 = tgtCode.get(i).toString();
				if (!str1.equals(str2)) {
					retVal = false;
					break;
				}
			}
		}
		return retVal;
	}

	public Random getRandom() {
		return random;
	}

	public void setRandom(Random random) {
		this.random = random;
	}

	public GAParameter getParameter() {
		return parameter;
	}

	public void setParameter(GAParameter parameter) {
		this.parameter = parameter;
	}

	public List<Chrom> getChroms() {
		return chroms;
	}

	public void setChroms(List<Chrom> chroms) {
		this.chroms = chroms;
	}

	public void setBestChroms(List<Chrom> bestChroms) {
		this.bestChroms = bestChroms;
	}

}